/**
 * @file
 * @brief 按键任务
 * @author
 * + 隐星魂 (Roy Sun) <xwos@xwos.tech>
 * @copyright
 * + Copyright © 2015 xwos.tech, All Rights Reserved.
 * > Licensed under the Apache License, Version 2.0 (the "License");
 * > you may not use this file except in compliance with the License.
 * > You may obtain a copy of the License at
 * >
 * >         http://www.apache.org/licenses/LICENSE-2.0
 * >
 * > Unless required by applicable law or agreed to in writing, software
 * > distributed under the License is distributed on an "AS IS" BASIS,
 * > WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * > See the License for the specific language governing permissions and
 * > limitations under the License.
 */

#include "board/std.h"
#include <xwos/osal/thd.h>
#include <xwos/osal/pm.h>
#include <xwos/osal/sync/sem.h>
#include <xwcd/ds/soc/gpio.h>
#include "RTD/xwds/device.h"
#include "Port_Ci_Icu_Ip.h"

#define BUTTON_THD_PRIORITY XWOS_SKD_PRIORITY_DROP(XWOS_SKD_PRIORITY_RT_MAX, 0)
struct xwos_thd button_thd;
xwos_thd_d button_thdd;
__xwcc_aligned(16) xwu8_t button_thd_stack[2048] = {0};
struct xwos_sem button_sem;

#define BUTTON_DEBOUNCING_DELAY (XWTM_MS(20))
#define BUTTON_GPIO_PORT XWDS_GPIO_PORT_D
#define BUTTON_GPIO_PIN XWDS_GPIO_PIN_15

#define BUTTON_ICU_PORT 3
#define BUTTON_ICU_CHANNEL 15

enum power_mode_em {
        PWRMODE_STANDBY,
        PWRMODE_RUNNING,
};

enum power_mode_em power_mode = PWRMODE_RUNNING;

static
void button_enable_detection(void)
{
        Port_Ci_Icu_Ip_EnableEdgeDetection(BUTTON_ICU_PORT, BUTTON_ICU_CHANNEL);
}

static
void button_disable_detection(void)
{
        Port_Ci_Icu_Ip_DisableDetection(BUTTON_ICU_PORT, BUTTON_ICU_CHANNEL);
}

static
void button_handle_event(void)
{
        if (PWRMODE_RUNNING == power_mode) {
                power_mode = PWRMODE_STANDBY;
                xwos_pm_suspend();
        } else {
                power_mode = PWRMODE_RUNNING;
        }
}

static
xwer_t button_get_event(void)
{
        xwer_t rc;
        xwsq_t in;

        rc = xwos_sem_wait(&button_sem);
        if (rc < 0) {
                goto err_sem_wait;
        }
        rc = xwos_cthd_sleep(BUTTON_DEBOUNCING_DELAY / 2);
        if (rc < 0) {
                goto err_sleep;
        }
        rc = XWOK;
        do {
                rc = xwos_cthd_sleep(BUTTON_DEBOUNCING_DELAY / 2);
                if (rc < 0) {
                        goto err_sleep;
                }
                rc = xwds_gpio_input(&rtdxwds_soc,
                                     BUTTON_GPIO_PORT,
                                     BUTTON_GPIO_PIN,
                                     &in);
                if (rc < 0) {
                        goto err_gpio_input;
                }
        } while (0 == in);
        button_handle_event();

err_gpio_input:
err_sleep:
        button_enable_detection();
err_sem_wait:
        return rc;
}

static
void button_suspend(void)
{
}

static
void button_resume(void)
{
}

void button_notification(void)
{
        button_disable_detection();
        if (XWOS_PM_STAGE_SUSPENDED == xwos_pm_get_stage()) {
                xwos_pm_resume();
        }
        xwos_sem_post(&button_sem);
}

xwer_t button_task(void * arg)
{
        xwer_t rc;

        xwds_gpio_req(&rtdxwds_soc, BUTTON_GPIO_PORT, BUTTON_GPIO_PIN);
        XWOS_UNUSED(arg);
        while (!xwos_cthd_shld_stop()) {
                if (xwos_cthd_shld_frz()) {
                        button_suspend();
                        xwos_cthd_freeze();
                        button_resume();
                } else {
                        rc = button_get_event();
                        if (XWOK == rc) {
                        } else {
                        }
                }
        }
        return XWOK;
}

void button_start(void)
{
        xwer_t rc;
        struct xwos_thd_attr attr;

        xwos_sem_init(&button_sem, 0, 1);
        xwos_thd_attr_init(&attr);
        attr.name = "button.thd";
        attr.stack = (xwstk_t *)button_thd_stack;
        attr.stack_size = sizeof(button_thd_stack);
        attr.priority = BUTTON_THD_PRIORITY;
        attr.detached = true;
        attr.privileged = true;
        rc = xwos_thd_init(&button_thd, &button_thdd,
                           &attr,
                           button_task,
                           NULL);
        if (XWOK == rc) {
                Port_Ci_Icu_Ip_Init(BUTTON_ICU_PORT, &Port_Ci_Icu_Ip_3_Config_PB_BOARD_InitPeripherals);
                Port_Ci_Icu_Ip_EnableNotification(BUTTON_ICU_PORT, BUTTON_ICU_CHANNEL);
        } else {
        }
}

void button_stop(void)
{
        xwer_t trc;
        xwer_t rc;

        Port_Ci_Icu_Ip_DisableNotification(BUTTON_ICU_PORT, BUTTON_ICU_CHANNEL);
        Port_Ci_Icu_Ip_DeInit(BUTTON_ICU_PORT);
        rc = xwos_thd_stop(button_thdd, &trc);
        if (XWOK == rc) {
                xwos_sem_fini(&button_sem);
        }
}
